#pragma once
#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include "Connection.hpp"
#include "./module/comm.hpp"
#include "./module/log.hpp"
using namespace MyLog;

class Channel : public Connection
{
public:
    Channel(int sockfd, InetAddr &client)
        :_sockfd(sockfd),
         _client(client)
    {
        // 将文件描述符设置为非阻塞
        nonBlock(sockfd);
    }
    ~Channel(){}

    int getSockfd() { return _sockfd; }

    void recver() override 
    {
        // 此时直接recv是不会阻塞的，但因为我们设置了ET和非阻塞
        // 我们必须一次性将所有的报文全都读上来————while
        // 但是我们无法保证我们读到的就是一个完整的报文————引入协议
        char buffer[1024];
        while(true) {
            ssize_t n = recv(_sockfd, buffer, sizeof(buffer) - 1, 0);
            if(n > 0) {
                buffer[n] = 0;
                _in_buffer += buffer; // 将其追加到该文件描述符的缓冲区中
            }
            else if (n == 0){
                excepter();
                return;
            }
            else {
                if(errno == EAGAIN || errno == EWOULDBLOCK) // 底层没有数据
                    break;
                else if(errno == EINTR) // 系统调用被信号中断
                    continue;
                else {
                    // 读取出错
                    excepter();
                    return;
                } 
            }
        }

        LOG(loglevel::DEBUG) << "inbuffer->\n " << _in_buffer;
        if(!_in_buffer.empty()) // 当缓冲区不为空，此时进行回调，让上层去处理报文
            _out_buffer += _handler(_in_buffer);
        if(!_out_buffer.empty())
            sender();
    } 
    void sender() override 
    {
        while(true) {
            ssize_t n = send(_sockfd, _out_buffer.c_str(), _out_buffer.size(), 0);
            if(n > 0) {
                _out_buffer.erase(0, n);
                if(_out_buffer.empty())
                    break;
            }
            else if(n == 0)
                break;
            else {
                if(errno == EAGAIN || errno == EWOULDBLOCK) // 缓冲区被写满了
                    break;
                else if(errno == EINTR) // 系统调用被信号中断
                    continue;
                else {
                    // 读取出错
                    excepter();
                    return;
                } 
            }
        }
        // 走到这里，有两种情况
        // 1.outbuffer为空
        // 2.底层缓冲区被写满了
        if(!_out_buffer.empty()) getPrivateData()->enableReadWrite(_sockfd, true, true);
        else getPrivateData()->enableReadWrite(_sockfd, true, false);
    } 
    void excepter() override 
    {
        // 不论出现任何异常，都已经被转换为了IO异常
        // 而当出现IO异常后，我们要做的就是取消epoll对该fd的关心
        // 并且从reactor的connection中删除该fd和其连接
        // 最后关闭连接
        getPrivateData()->delConnection(_sockfd);
    }
private:
    int _sockfd;
    // 在读取文件描述符时，不能保证一次性读完整，当我们第二次读取时，第一次读取的内容就已经被清空了
    // 而且，所有的文件描述符共享一个缓冲区，这样太不优雅了
    // 这里对所有的文件描述符进行封装————使其有自己独立的输入和输出缓冲区，这样就算第一次没有读完，后序读上来的数据可以拼接到缓冲区中
    std::string _in_buffer;
    std::string _out_buffer;
    // client info
    InetAddr _client;
};